package me.gijspennings.jumpandduck;

import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
import me.gijspennings.jumpandduck.entity.mob.Player;
import me.gijspennings.jumpandduck.graphics.Screen;
import me.gijspennings.jumpandduck.graphics.Sprite;
import me.gijspennings.jumpandduck.input.Input;
import me.gijspennings.jumpandduck.level.CaveLevel;
import me.gijspennings.jumpandduck.level.Level;

public class Game extends Canvas implements Runnable {
    private static final long serialVersionUID = 1L;
    
    public final static String NAME = "Jump N' Duck";
    public final static int WIDTH = 256;
    public final static int HEIGHT = WIDTH / 16 * 9;
    public final static int SCALE = 4;
    
    private boolean running = false;
    private Thread thread;
    private JFrame window;
    private Screen screen;
    private BufferedImage image = new BufferedImage(Game.WIDTH, Game.HEIGHT, BufferedImage.TYPE_INT_RGB);
    private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
    private Input input;
    private Level level;
    
    public Game() {
        window = new JFrame();
        screen = new Screen(Game.WIDTH, Game.HEIGHT);
        input = new Input();
        level = new CaveLevel(new Player(level, 1 * Sprite.DEFAULT_SIZE, 5 * Sprite.DEFAULT_SIZE), 2);
        
        setPreferredSize(new Dimension(Game.WIDTH * Game.SCALE, Game.HEIGHT * Game.SCALE));
        addKeyListener(input.keyboard);
    }
    
    public synchronized void start() {
        running = true;
        thread = new Thread(this);
        thread.start();
    }
    public synchronized void stop() {
        running = false;
        try {
            thread.join(); // Close the thread (wait for the thread to die)
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void run() {
        long timer = System.nanoTime();
        int updates = 0;
        int frames = 0;
        
        final double timePerUpdate = 1000000000.0 / 60.0; // 1000000000 nanoseconds = 1 second
        long lastTime = System.nanoTime();
        long currentTime;
        double delta = 0.0;
        
        while(running) {
            currentTime = System.nanoTime();
            delta += (currentTime - lastTime) / timePerUpdate;
            lastTime = currentTime;
            while (delta >= 1) {
                update();
                updates++;
                delta--;
            }
            
            render();
            frames++;
            
            if (System.nanoTime() - timer > 1000000000) {
                window.setTitle(Game.NAME + " | " + updates + " ups, " + frames + " fps");
                timer = System.nanoTime();
                updates = 0;
                frames = 0;
            }
        }
        stop();
    }
    private void update() {
        input.update();
        level.update(screen);
    }
    private void render() {
        BufferStrategy bs = getBufferStrategy();
        if (bs == null) {
            createBufferStrategy(3); // 3 = triple buffering
            return;
        }
        
        screen.clear();
        level.render(screen);
        for (int i = 0; i < pixels.length; i++) pixels[i] = screen.pixels[i];
        
        Graphics g = bs.getDrawGraphics();
        g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
        g.dispose(); // Clean the memory of the graphics
        bs.show(); // Switch to the next buffer
    }
    
    public static void main(String[] args) {
        Game game = new Game();
        game.window.setResizable(false);
        game.window.setTitle(Game.NAME);
        game.window.add(game); // Add the canvas component to the window
        game.window.pack(); // Set the size of the window to the size of the canvas
        game.window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Exit the application when the window has been closed
        game.window.setLocationRelativeTo(null); // null = center of the screen
        game.window.setVisible(true);
        game.start();
    }
    
}
